import { useCallback, useEffect, useRef, useState } from 'react';
import { unstable_batchedUpdates } from 'react-dom';
import { RequestData, UseFetchDataAction, UseFetchProps, PageInfo, postDataPipeline } from "./typing";
import { useMountMergeState, usePrevious, useDebounceFn } from '@/utils';

/**
 * 组合用户的配置和默认值
 *
 * @param param0
 */
const mergeOptionAndPageInfo = ({ pageInfo }: UseFetchProps) => {
    if (pageInfo) {
        const { current, defaultCurrent, pageSize, defaultPageSize } = pageInfo;
        return {
            current: current || defaultCurrent || 1,
            total: 0,
            pageSize: pageSize || defaultPageSize || 20,
        };
    }
    return { current: 1, total: 0, pageSize: 20 };
};

const FetchData = <T extends RequestData<any>> (
    getData: undefined | ((params?: { pageSize: number; current: number }) => Promise<T>),
    defaultData: any | undefined,
    options: UseFetchProps
): UseFetchDataAction => {
    /**
     * 用于保存组件是否被卸载的状态的引用
     * @type {React.MutableRefObject<boolean>}
     */
    const umountRef = useRef<boolean>(false);

    /**
    * useFetchData 钩子的配置项
    * @typedef {object} UseFetchProps
    * @property {boolean} [onLoad=false] 是否在页面加载时执行请求，默认为 false
    * @property {boolean} [manual=false] 是否手动触发请求，默认为 false
    * @property {number | boolean} [polling=false] 是否开启轮询，可以为数字表示轮询的时间间隔，也可以为 true 表示开启默认时间为 1s 的轮询
    * @property {function} [onRequestError] 请求错误的回调函数
    * @property {number} [debounceTime=20] 防抖时间，单位为毫秒，默认为 20ms
    */
    const {
        onLoad,
        manual,
        completeManual = false,
        polling,
        onRequestError,
        debounceTime = 20,
        effects = [],
        ...rest
    } = options || {};

    /** 是否首次加载的指示器 */
    const manualRequestRef = useRef<boolean>(manual);

    // 用于保存 AbortController 实例的引用，方便需要时进行请求的取消操作
    const abortRef = useRef<AbortController | null>(null);

    // 用于存储最新的数据
    const [tableDataList, setTableDataList] = useMountMergeState(defaultData, {
        value: options?.dataSource,
        onChange: options.onDataSourceChange,
    });

    // 数据加载状态
    const [loading, setLoading] = useMountMergeState<boolean>(false, {
        value: options?.loading,
        onChange: options.onLoadingChange,
    });


    /**
     * 表示页面信息的类型  useMountMergeState 钩子的初始值和参数
     * @typedef {object} PageInfo
     * @property {number} current 当前页码
     * @property {number} pageSize 页面大小
     * @property {number} total 数据总量
     * @type {[PageInfo, React.Dispatch<React.SetStateAction<PageInfo>>]}
     */
    const [pageInfo, setPageInfoState] = useMountMergeState<PageInfo>(
        () => mergeOptionAndPageInfo(options),
        {
            onChange: options?.onPageInfoChange,
        },
    );

    // 设置分页信息
    const setPageInfo = (changePageInfo: PageInfo) => {
        if (
            changePageInfo.current !== pageInfo.current ||
            changePageInfo.pageSize !== pageInfo.pageSize ||
            changePageInfo.total !== pageInfo.total
        ) {
            setPageInfoState(changePageInfo);
        }
    };


    const setDataAndLoading = (newData: any[], dataTotal?: number) => {
        unstable_batchedUpdates(() => {
            setTableDataList(newData);
            if (pageInfo?.total !== dataTotal) {
                setPageInfo({
                    ...pageInfo,
                    total: dataTotal || newData.length
                });
            }
        });
    }

    /**
     * 上一页的页码
     * @type {number}
     */
    const prePage = usePrevious(pageInfo?.current);

    /**
     * 上一页的页面大小
     * @type {number}
     */
    const prePageSize = usePrevious(pageInfo?.pageSize);

    const requestFinally = useCallback(() => {
        unstable_batchedUpdates(() => {
            setLoading(false);
        });
    }, []);

    const totalRef = useRef(0);

    // 请求数据
    const fetchList = async () => {
        if (!getData) {
            return;
        }
        // 需要手动触发的首次请求
        if (manualRequestRef.current) {
            manualRequestRef.current = false;
            return;
        }
        // 手动模式不自动请求
        setLoading(true);

        const { pageSize, current } = pageInfo || {};
        try {
            const pageParams = options?.pageInfo !== false ? { pageSize, current } : undefined;
            const {
                data,
                success,
                total = 0,
                ...rest
            } = (await getData?.(pageParams)) || {};
            // 如果失败了，直接返回，不走剩下的逻辑了
            if (success === false) return [];

            const responseData = postDataPipeline(data!,
                [options.postData].filter((item) => item) as any,
            );
            // 设置表格数据
            setDataAndLoading(responseData, total);
            onLoad?.(responseData, rest);

            totalRef.current = total;

            return responseData;
        } catch (error) {
            if (onRequestError == null || onRequestError === undefined) {
                throw new Error(error as string);
            }
            if (onRequestError) {
                onRequestError(error as Error);
            }
            if (tableDataList === undefined) {
                setTableDataList([]);
            }
        } finally {
            requestFinally();
        }
        return [];
    }

    const fetchListDebounce = useDebounceFn(async () => {
        if (!getData) {
            return;
        }

        const abort = new AbortController();
        abortRef.current = abort;


        const result = await fetchList();
        return result;

    }, debounceTime || 30);

    /**
    * 取消请求
    */
    const abortFetch = () => {
        abortRef.current?.abort();
        fetchListDebounce.cancel();
        requestFinally();
    };

    useEffect(() => {
        umountRef.current = false;
        return () => {
            umountRef.current = true;
        };
    }, []);

    /** PageIndex 改变的时候自动刷新 */
    useEffect(() => {
        if (completeManual) {
            return;
        }
        const { current, pageSize } = pageInfo || {};
        // 如果上次的页码为空或者两次页码等于是没必要查询的
        // 如果 pageSize 发生变化是需要查询的，所以又加了 prePageSize
        if (
            (!prePage || prePage === current) &&
            (!prePageSize || prePageSize === pageSize)
        ) {
            return;
        }

        if (
            (options.pageInfo && tableDataList && tableDataList?.length > pageSize) ||
            0
        ) {
            return;
        }

        // 如果 list 的长度大于 pageSize 的长度
        // 说明是一个假分页
        // (pageIndex - 1 || 1) 至少要第一页
        // 在第一页大于 10
        // 第二页也应该是大于 10
        if (
            current !== undefined &&
            tableDataList &&
            tableDataList.length <= pageSize
        ) {
            abortFetch();
            fetchListDebounce.run(false);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [pageInfo?.current]);

    // pageSize 修改后返回第一页
    useEffect(() => {
        if (completeManual) {
            return;
        }
        if (!prePageSize) {
            return;
        }
        abortFetch();
        fetchListDebounce.run(false);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [pageInfo?.pageSize]);

    useEffect(() => {
        // 如果不是 manual 模式，则不执行请求。
        if (!completeManual) {
            abortFetch();
            fetchListDebounce.run(false);
        }
        if (!manual) {
            // 如果 manual 标志未设置，则将 manualRequestRef 设置为 false。
            // 用于跟踪当前的请求是否是手动发起的。
            manualRequestRef.current = false;
        }
        return () => {
            abortFetch();
        };
    }, [...effects, manual]);

    return {
        /**
         * 表格的数据列表。
         * @type {DataSource[]}
         */
        dataSource: tableDataList!,
        /**
         * 用于设置表格数据列表的 setter 函数。
         * @type {function}
         * @param {DataSource[]} list - 更新后的表格数据列表。
         */
        setDataSource: setTableDataList,
        /**
         * 表示表格是否正在加载数据的标志。
         * @type {boolean}
         */
        loading: loading,
        /**
         * 重新加载表格数据的函数。
         * @type {function}
         * @async
         * @returns {Promise<boolean>} - 数据重新加载完成后解决为 true 的 Promise。
         */
        reload: async () => {
            abortFetch();
            // 对于手动 reload，我们需要强制执行请求，即使在 manualRequest 模式下
            manualRequestRef.current = false;
            const responseData = await fetchListDebounce.run(false);;
            return responseData;
        },

        completeManualRequest: async () => {
            abortFetch();
            manualRequestRef.current = false;
            const result = await  fetchListDebounce.run(false);
            return {
                data: result,
                total: totalRef.current,
            };
        },

        /**
         * 当前的分页信息。
         * @type {Object}
         * @prop {number} current - 当前页码。
         * @prop {number} total - 总数据数量。
         * @prop {number} pageSize - 每页数据数量。
         */
        pageInfo,
        /**
         * 重置分页信息为其初始值的函数。
         * @type {function}
         * @async
         * @returns {Promise<void>} - 重置完成后解决的 Promise。
         */
        reset: async () => {
            const { pageInfo: optionPageInfo } = options || {};
            const { defaultCurrent = 1, defaultPageSize = 20 } = optionPageInfo || {};
            const initialPageInfo = {
                current: defaultCurrent,
                total: 0,
                pageSize: defaultPageSize,
            };
            setPageInfo(initialPageInfo);
        },
        /**
         * 更新分页信息的函数。
         * @type {function}
         * @async
         * @param {Object} info - 新的分页信息。
         * @prop {number} [current] - 新的当前页码。
         * @prop {number} [total] - 新的总数据数量。
         * @prop {number} [pageSize] - 新的每页数据数量。
         * @returns {Promise<void>} - 更新完成后解决的 Promise。
         */
        setPageInfo: async (info) => {
            setPageInfo({
                ...pageInfo,
                ...info,
            });
        },
    };
}

export default FetchData;